#!/usr/bin/python
#-*- coding: utf-8 -*-

"""
description	:2013 ComputerVision Schwanecke Uebungsblatt 05
date			:2013
version		: 0.1

tested on       :
                Distributor ID:	Ubuntu
                Description:	Ubuntu 12.04.3 LTS
                Release:	12.04
                Codename:	precise
python_version	:Python 2.7.3

usage		: python a1.1_a1.2.py <imagefile>

"""
import sys
import numpy as np
import cv2


class Pic(object):
    """Ein Bild auf dem verschiedene Regions-Bearbeitungen moeglich sind

    TODO:
    Prozess Beenden wenn alle Fenster geschlossen werden

    """
    id_counter = 0

    def __init__(self, name, win_name):
        Pic.id_counter += 1
        self.id = Pic.id_counter
        self.name = name
        self.win_name = win_name
        self.size_of_one_pixel = 0.085

        # Farbe
        img = self.read_image(name)
        #img = np.invert(img)
        self.img = self.do_binary(img, 127, 255)  # 1 Kanal Binarbild
        self.color = (0, 0, 0)
        self.num_of_black_pixel = 0

        # ROI
        self.roi = None
        self.roi_bbox = []
        self.klick_points = []
        self.roi_centroid = ()
        self.roi_num_of_black_pixel = 0
        self.roi_area_of_black_pixel = 0.0
        self.roi_top_left = ()
        self.roi_bottom_right = ()

        # Maus-Callback-Funktion registrieren
        cv2.setMouseCallback(self.win_name, self.on_mouse)
        self.clicks = 0


    #  TODO: Sonderfaelle mit Rand abfangen
    def get_black_pixel(self, img):
        """Zaehlt alle schwarzen Pixel im Bild. -> (Flaeche, Anzahl, [schwarze_pixel])"""
        # Herausfiltern der schwarzen Pixel
        condition = np.equal(img, 0)
        ext = img[condition]
        num_black_pixel = len(ext)
        area_of_black_pixel = num_black_pixel * self.size_of_one_pixel

        # Pixelkoordinaten aller schwarzen Pixel im ROI
        black_pixel = zip(*np.nonzero(img < 1))
        return area_of_black_pixel, num_black_pixel, black_pixel


    def do_binary(self, img, thresh, maxval, method=cv2.THRESH_BINARY):
        """Binarisiert ein Bild anhand eines Schwellwertes und mitgegebener Methode."""
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        threshold, img_bin = cv2.threshold(img_gray, thresh, maxval, method)
        return img_bin

    #  TODO: Hübscher machen cv2Box oder so
    def draw_bbox(self, bbox):
        """Zeichnet Bondingbox anhand 2er 2D Koordinaten roi=[(x0,y0),(x1,y1)]
        @param bbox: [(x_min, y_min), (x_max, y_max)]

        """
        top_left = bbox[0]
        bottom_right = bbox[1]
        top_right = bbox[1][0], bbox[0][1]
        bottom_left = bbox[0][0], bbox[1][1]

        # Die zu verbindenden Eckpunkte
        edges = [(top_left, top_right),
                 (top_right, bottom_right),
                 (bottom_right, bottom_left),
                 (bottom_left, top_left)]

        for edge in edges:
            x1, y1 = edge[0][0], edge[0][1]
            x2, y2 = edge[1][0], edge[1][1]
            self.draw_line(self.win_name, self.img, (x1, y1), (x2, y2))


    def get_roi(self, klick_points, img):
        """Gibt Bild und Boundingbox der der durch klick_points definierten Boundingbox zurueck
        -> [ROI, roi_boundingbox]

        """
        roi_bbox = self.get_bbox(klick_points)
        ax, ay = roi_bbox[0][0], roi_bbox[0][1]
        bx, by = roi_bbox[1][0], roi_bbox[1][1]
        roi = img[ay+1:by, ax+1:bx]
        return roi, roi_bbox


    def get_bbox(self, points):
        """Berrechnet Boundingbox einer 2D-Punkte-Liste"""
        # numpy array draus machen
        points = np.array(points)

        # Teile in 2 Haelften entlang Achse 1 (Y)
        split = np.split(points, 2, 1)

        x_vals = split[0]
        y_vals = split[1]

        # Die einzelnen Werte aus den einelementeigen ndarrays holen mit item(0)
        x_min, x_max = min(x_vals).item(0), max(x_vals).item(0)
        y_min, y_max = min(y_vals).item(0), max(y_vals).item(0)
        self.roi_top_left = (x_min, y_min)
        self.roi_bottom_right = (x_max, y_max)
        return [(x_min, y_min), (x_max, y_max)]


    def read_image(self, file, method=cv2.CV_LOAD_IMAGE_COLOR):
        """Liest eine Bilddatei von Platte in gewuenschtem Farbmodus"""
        return cv2.imread(file, method)


    def show(self, img=None, windowname=None):
        """Zeigt sich oder ein cv2-image in einem neue Fenster an"""
        img = self.img if img is None else img
        windowname = self.win_name if windowname is None else windowname
        cv2.namedWindow(windowname, cv2.CV_WINDOW_AUTOSIZE)
        cv2.imshow(windowname, img)


    def draw_line(self, window, image, a, b, color=None):
        """Zeichnet eine Linie zwischen den Pixeln a,b"""
        color = self.color if color is None else color
        cv2.line(image, a, b, color)
        cv2.imshow(window, image)  # repaint


    def get_centroid(self, roi, binaryImage=True #  alle Werte != 0 werden zu 1
        ):
        """Gibt den Schwerpunkt (Xs,Ys) eines (grauwert) Bildes zurueck"""
        #roi = np.array(roi, dtype=np.uint8)
        moments = cv2.moments(roi, binaryImage)

        centroid_x = int(moments['m10']/moments['m00'])
        centroid_y = int(moments['m01']/moments['m00'])
        centroid = centroid_x, centroid_y

        cv2.circle(self.img, centroid, 10, (0, 0, 0), -1)
        cv2.imshow(self.win_name, self.img)
        # TODO: ncohmal vergleichen und checken 'Zaunpfahlproblem?'
        print 'Schwerpunkt im Gesamtbild: ', centroid
        print 'Schwerpunkt im ROI: ', (centroid_x + self.roi_top_left[0], centroid_y + self.roi_top_left[1])
        return centroid

    def get_contours(self, img):
        #imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
        #ret,thresh = cv2.threshold(img,127,255,0)
        contours, hierarchy = cv2.findContours(img,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
        return contours

    # Maus-Callback function
    def on_mouse(self, event, x, y, flags, param):
        """Callback-Funktion fuer Mouseevents"""
        if event == cv2.EVENT_LBUTTONDOWN:
            self.clicks += 1
            if self.clicks < 3:
                self.klick_points.append((x, y))
                cv2.imshow(self.win_name, self.img)  # repaint

                # 2.er Klick
                if self.clicks == 2:
                    self.roi, self.roi_bbox = self.get_roi(self.klick_points, self.img)
                    self.roi_area_of_black_pixel, self.roi_num_of_black_pixel, self.black_pixel = self.get_black_pixel(self.roi)
                    print 'Schwarz:\t{0}'.format(pic.roi_num_of_black_pixel)
                    print u'Fläche:\t{0}mm²'.format(pic.roi_area_of_black_pixel)
                    self.draw_bbox(self.roi_bbox)
                    #self.show(self.roi, "ROI") TODO: einkommentieren für Abgabe
                    # findContours veraendert das verwendete Bild!!
                    contours, hierarchy = cv2.findContours(self.roi, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
                    cnt = contours[0]
                    self.roi_centroid = self.get_centroid(self.roi)

                    x,y,w,h = cv2.boundingRect(self.roi)
                    cv2.rectangle(self.img,(x,y),(x+w,y+h),self.color,2)


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print "--> usage: python a1.1_a1.2.py <imagefile>"
        sys.exit(0)
    WIN_NAME = PIC_NAME =  sys.argv[1]

    # Fenster erstellen
    cv2.namedWindow(WIN_NAME, cv2.CV_WINDOW_AUTOSIZE)

    pic = Pic(PIC_NAME, WIN_NAME)
    pic.show()

    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    



    

